#include "ty_timer.h"
#include "co.h"

/*********************************************************************
 * CONSTANT
 */
#define TY_TIMER_MAX_NUM                    20

enum {
    TY_SUCCESS = 0x00,
    TY_ERR_NO_MEM,
    TY_ERR_NOT_FOUND,
    TY_ERR_INVALID_PARAM,
    TY_ERR_UNKNOWN,
};

/*********************************************************************
 * STRUCT
 */
typedef struct om_timer_s
{
    co_timer_t tid;
    uint32_t delay;
    co_timer_mode_t mode;
    co_timer_callback_t cb;
} om_timer_t;

typedef struct ty_timer_s
{
    om_timer_t *p_timer;
    uint8_t     is_occupy;
} ty_timer_t;

/*********************************************************************
 * VARIABLE
 */
ty_timer_t ty_timer_pool[TY_TIMER_MAX_NUM] = {0};

/*********************************************************************
 * FUNCTION
 */
/*********************************************************
FN: 
*/
static om_timer_t * acquire_timer(void)
{
    for (uint8_t i=0; i<TY_TIMER_MAX_NUM; i++)
    {
        if (ty_timer_pool[i].is_occupy == 0)
        {
            ty_timer_pool[i].p_timer = co_malloc(sizeof(om_timer_t));
            if (ty_timer_pool[i].p_timer != NULL)
            {
                ty_timer_pool[i].is_occupy = 1;
                return ty_timer_pool[i].p_timer;
            }
        }
    }
    return NULL;
}

/*********************************************************
FN: 
*/
static int32_t release_timer(void* timer_id)
{
    for (uint8_t i=0; i<TY_TIMER_MAX_NUM; i++)
    {
        if (timer_id == ty_timer_pool[i].p_timer)
        {
            ty_timer_pool[i].is_occupy = 0;
            return i;
        }
    }
    return -1;
}

/*********************************************************
FN: 
*/
static int32_t find_timer(void* timer_id)
{
    for (uint8_t i=0; i<TY_TIMER_MAX_NUM; i++)
    {
        if (timer_id == ty_timer_pool[i].p_timer
            && ty_timer_pool[i].is_occupy == 1)
        {
            return i;
        }
    }
    return -1;
}

/*********************************************************
FN: 
*/
uint32_t ty_timer_create(void** p_timer_id, uint32_t ms, ty_timer_mode_t mode, ty_timer_handler_t handler)
{
    om_timer_t *timer = acquire_timer();
    if (timer != NULL)
    {
        timer->delay = ms;
        timer->mode = (co_timer_mode_t)mode;
        timer->cb = (co_timer_callback_t)handler;
        *p_timer_id = (void*)timer;
        return TY_SUCCESS;
    }
    else
    {
        TY_PRINTF("Error: App timer is used up");
        return TY_ERR_NO_MEM;
    }
}

/*********************************************************
FN: 
*/
uint32_t ty_timer_delete(void* timer_id)
{
    if (release_timer(timer_id) > 0)
    {
        om_timer_t *timer = (om_timer_t*)timer_id;
        co_timer_del(&timer->tid);
        co_free(timer);
        timer = NULL;
        return TY_SUCCESS;
    }
    else
    {
        TY_PRINTF("Error: Timer id is invalid");
        return TY_ERR_INVALID_PARAM;
    }
}

/*********************************************************
FN: 
*/
uint32_t ty_timer_start(void* timer_id)
{
    if (find_timer(timer_id) >= 0)
    {
        om_timer_t *timer = (om_timer_t*)timer_id;
        co_timer_set(&timer->tid, timer->delay, timer->mode, timer->cb, NULL);
        return TY_SUCCESS;
    }
    else
    {
        TY_PRINTF("Error: Timer id is not found");
        return TY_ERR_NOT_FOUND;
    }
}

/*********************************************************
FN: 
*/
uint32_t ty_timer_stop(void* timer_id)
{
    if (find_timer(timer_id) >= 0)
    {
        om_timer_t *timer = (om_timer_t*)timer_id;
        co_timer_del(&timer->tid);
        return TY_SUCCESS;
    }
    else
    {
        TY_PRINTF("Error: Timer id is not found");
        return TY_ERR_NOT_FOUND;
    }
}

/*********************************************************
FN: 
*/
uint32_t ty_timer_restart(void* timer_id, uint32_t ms)
{
    if (find_timer(timer_id) >= 0)
    {
        om_timer_t *timer = (om_timer_t*)timer_id;
        co_timer_del(&timer->tid);
        timer->delay = ms;
        co_timer_set(&timer->tid, timer->delay, timer->mode, timer->cb, NULL);
        return TY_SUCCESS;
    }
    else
    {
        TY_PRINTF("Error: Timer id is not found");
        return TY_ERR_NOT_FOUND;
    }
}
